#ifndef LOGO_EXPRESSION
#define LOGO_EXPRESSION
#include "Collections/List.h"
#include "Collections/Dictionary.h"
#include "Basic/String.h"
#include "Basic/Pointer.h"
#include "lex.h"

namespace logo
{
	using namespace zl;
	using namespace zl::collections;

	class Expression;
	class ArithmeticExpression;
	class AssignmentExpression;
	class CompareExpression;
	class LoopExpression;
	class FuncExpression;

	enum class ExpressionType
	{
		Unknow,
		Arithmetic,
		Assignment,
		Compare,
		Select,
		Loop,
		Defunc,
		Cmd,
		Func
	};

	class ExpressionError
	{
	public:
		Token         info;
	    std::wstring       message;
	};


	class Expression
	{
	public:
		ExpressionType type;
	public:
		Expression();
		virtual ~Expression();
	};

	class ArithmeticExpression :public Expression
	{
	public:
		double         value;
	public:
		ArithmeticExpression();
		~ArithmeticExpression();
	};

	class AssignmentExpression :public Expression
	{
	public:
		std::wstring         name;
		double          value;
	public:
		AssignmentExpression();
		AssignmentExpression(std::wstring _name, double _value);
		~AssignmentExpression();
	};

	class CompareExpression :public Expression
	{
	public:
		bool                   value;
	public:
		CompareExpression();
		~CompareExpression();
	};

	class  CmdExpression :public Expression
	{
	public:
		List<double>           args;
		std::wstring           name;
	public:
		CmdExpression();
		~CmdExpression();
	};

	class SelectExpression :public Expression
	{
	public:
		bool                       value;
		List<Ptr<Expression>>      tbranch;
		List<Ptr<Expression>>      fbranch;
	public:
		SelectExpression();
		~SelectExpression();
	};

	class  FuncExpression :public Expression
	{
	public:
		List<double>                args;
		std::wstring                name;
		Dictionary<std::wstring, AssignmentExpression> env;
		bool                        isend;
		List<Ptr<Expression>>       proc;
	public:
		FuncExpression();
		~FuncExpression();
	};

	class  LoopExpression :public Expression
	{
	public:
		zint                   loops;
		List<Ptr<Expression>>  proc;
	public:
		LoopExpression();
		~LoopExpression();
	};


	class  DefFuncExpression :public Expression
	{
	public:
		std::wstring                  name;
		zint                          start;
		zint                          end;
		List<std::wstring>            args;
	public:
		DefFuncExpression();
		~DefFuncExpression();
	};

	extern Ptr<ArithmeticExpression>    ParseArithmeticExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<ArithmeticExpression>    ParseTerm(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<ArithmeticExpression>    ParseFactor(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseAssignmentExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseCmdExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseUseFuncExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseCompareExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseSelectExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseLoopExpression(List<Token>& tokens, zint& index,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern Ptr<Expression>              ParseDefFuncExpression(List<CodeLine>& lines, zint& rows,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
	extern bool                         ParseUnit(List<Token>& tokens, zint& index, List<Ptr<Expression>>& expressions,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);

	extern bool                         ParseArea(List<CodeLine>& lines, zint& rows, List<Ptr<Expression>>& expressions,
		Dictionary<std::wstring, AssignmentExpression>& env,
		List<ExpressionError>& errs);
};
#endif
